#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h> 

#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

#define MAX_EVENTS 	1000
#define MAX_LINE 	1024
#define PORT 		8080

#define xlog(fmt,arg...) printf("[%s-%s:%d] "fmt, __FILE__, __FUNCTION__, __LINE__, ##arg)

struct epoll_event ev, events[MAX_EVENTS];

// set socket as nonblocking
void setnonblocking(int sockfd) {
	int opts;

	opts = fcntl(sockfd, F_GETFL);
	if(opts < 0) {
		xlog("fcntl_getfl: %s\n", strerror(errno));
		exit(1);
	}
	opts = (opts | O_NONBLOCK);
	if(fcntl(sockfd, F_SETFL, opts) < 0) {
		xlog("fcntl_setfl: : %s\n", strerror(errno));
		exit(1);
	}
}

// read data from buffer (ET)
int read_conn(int sockfd, char* buf) {
	int n = 0, nread = 0;

	while ((nread = read(sockfd, buf + n, BUFSIZ-1)) > 0)
		n += nread;

	if (nread == -1 && errno != EAGAIN) {
		xlog("read error: %s\n", strerror(errno));
		return -1;
	}
	else if (nread == -1 && errno == EAGAIN) {
		xlog("read complete: %s\n", strerror(errno));
		buf[n] = '\0';
        printf("%s\n", buf);
		return n;
	}
	else if (nread == 0) {
		xlog("socket closed: %s\n", strerror(errno));
		return 0;
	}
}

// write data to buffer (ET)
int write_conn(int sockfd, char* buf, int n) {
	int nwrite = 0, data_size = n;
	int flag = 1;

	while (n > 0) {
		nwrite = write(sockfd, buf + data_size - n, n);
		if (nwrite < n) {
			if (nwrite == -1 && errno != EAGAIN) {
				xlog("write error: %s\n", strerror(errno));
				break;
			}
			else if (nwrite == -1 && errno == EAGAIN) {
				xlog("write complete: %s\n", strerror(errno));
				flag = 1;
				break;
			}
		}

		n -= nwrite;
	}

	if (n == 0 || flag == 0) 
		xlog("write complete: %s\n", strerror(errno));

	return data_size - n;
}

// accept connections (ET)
int accept_conn(int epfd, int sockfd) {
	int conn_sock;
	struct sockaddr_in remote;
	int addrlen;

	while ((conn_sock = accept(sockfd,(struct sockaddr *)&remote, 
									               &addrlen)) > 0) {
		xlog("accept %s:%d\n", inet_ntoa(remote.sin_addr), ntohs(remote.sin_port));
		setnonblocking(conn_sock);
		ev.events = EPOLLIN | EPOLLET;
		ev.data.fd = conn_sock;
		if (epoll_ctl(epfd, EPOLL_CTL_ADD, conn_sock, &ev) == -1) {
			xlog("epoll_ctl_add: %s\n", strerror(errno));
			exit(-1);
		}
	}

	if (conn_sock == -1) {
		if (errno != EAGAIN && errno != ECONNABORTED && \
								errno != EPROTO && errno != EINTR) {
			xlog("accept_error:%s\n", strerror(errno));
			exit(-1);
		}
	}

	return 0;
}

int main(){
    int listenfd, nfds, epfd;
    int i, fd;
    struct sockaddr_in local, remote;
    char buf[BUFSIZ];

    // create socket
    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        xlog("socket: %s\n", strerror(errno));
        exit(1);
    }

    setnonblocking(listenfd);
    bzero(&local, sizeof(local));
    local.sin_family = AF_INET;
    local.sin_addr.s_addr = htonl(INADDR_ANY);;
    local.sin_port = htons(PORT);
    if (bind(listenfd, (struct sockaddr *) &local, sizeof(local)) < 0) {
        xlog("bind: %s\n", strerror(errno));
        exit(-1);
    }

    listen(listenfd, 20);

    if ((epfd = epoll_create(MAX_EVENTS)) == -1) {
        xlog("epoll_create: %s\n", strerror(errno));
        exit(-1);
    }

    ev.events  = EPOLLIN;
    ev.data.fd = listenfd;
    if (epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev) == -1) {
        xlog("epoll_ctl_listen_sock: %s\n", strerror(errno));
        exit(-1);
    }

    while (1) {
        nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);
        if (nfds == -1) {
            xlog("epoll_wait: %s\n", strerror(errno));
            exit(-1);
        }

        for (i = 0; i < nfds; ++i) {
            fd = events[i].data.fd;
            if (fd == listenfd) {
            	/*
            		int conn_sock;
				struct sockaddr_in remote;
				int addrlen;

				while ((conn_sock = accept(listenfd,(struct sockaddr *)&remote, 
												               &addrlen)) > 0) {
					setnonblocking(conn_sock);
					ev.events = EPOLLIN | EPOLLET;
					ev.data.fd = conn_sock;
					if (epoll_ctl(epfd, EPOLL_CTL_ADD, conn_sock, &ev) == -1) {
						xlog("epoll_ctl_add: %s\n", strerror(errno));
						exit(-1);
					}

					xlog("accept %s:%d\n", inet_ntoa(remote.sin_addr), ntohs(remote.sin_port));
				}

				if (conn_sock == -1) {
					if (errno != EAGAIN && errno != ECONNABORTED && \
											errno != EPROTO && errno != EINTR) {
						xlog("accept_error:%s\n", strerror(errno));
						exit(-1);
					}
				}
			*/
                accept_conn(epfd, listenfd);
            }
            else if (events[i].events & EPOLLIN) {
                read_conn(fd, buf);

                ev.data.fd = fd;
                ev.events = events[i].events | EPOLLOUT;
                if (epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev) == -1) {
                    xlog("epoll_ctl_mod: %s\n", strerror(errno));
                    exit(-1);
                }
            }
            else if (events[i].events & EPOLLOUT) {
                char buf[MAX_LINE] = {0};
                sprintf(buf, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\nHello World", 11);
                write_conn(fd, buf, strlen(buf));
                close(fd);
            }
        }
    }

    return 0;
}
